<?php
// This file should be in UTF8 without BOM - Accents examples: éèê
// +----------------------------------------------------------------------+
// | Copyright (c) 2004-2015 Advisto SAS, service PEEL - contact@peel.fr  |
// +----------------------------------------------------------------------+
// | This file is part of PEEL Shopping 7.2.1, which is subject to an	  |
// | opensource GPL license: you are allowed to customize the code		  |
// | for your own needs, but must keep your changes under GPL			  |
// | More information: https://www.peel.fr/lire/licence-gpl-70.html		  |
// +----------------------------------------------------------------------+
// | Author: Advisto SAS, RCS 479 205 452, France, https://www.peel.fr/	  |
// +----------------------------------------------------------------------+
// $Id: Product.php 44077 2015-02-17 10:20:38Z sdelaporte $
if (!defined('IN_PEEL')) {
	die();
}

/**
 *
 * @brief La classe Product porte l'ensemble des fonctionnalités propres aux produits vendus avec PEEL
 * Ses méthodes permettent de récupérer notamment le prix formatté ou non du produit en tenant compte de toutes les réductions éventuelles
 * @package PEEL
 * @author PEEL <contact@peel.fr>
 * @copyright Advisto SAS 51 bd Strasbourg 75010 Paris https://www.peel.fr/
 * @version $Id: Product.php 44077 2015-02-17 10:20:38Z sdelaporte $
 * @access public
 */
class Product {
	var $id = null;
	var $technical_code = null;
	var $name = null;
	var $descriptif = null;
	var $description = null;
	var $meta_titre = null;
	var $meta_desc = null;
	var $meta_key = null;
	var $lang = null;
	var $poids = null;
	var $volume = null;
	var $position = null;
	var $display_price_by_weight = null;
	var $prix_barre = null;
	var $prix_barre_ht = null;
	var $prix = null;
	var $prix_ht = null;
	var $prix_achat = null;
	var $reference = null;
	var $ean_code = null;
	var $etat = null;
	var $on_estimate = null;
	// prix_revendeur is the price for resellers : it is stored with taxes included, even if we will usually display it without taxes
	var $prix_revendeur = null;
	var $promotion = null;
	var $tva = null;
	var $points = null;
	var $default_image = null;
	var $image1 = null;
	var $image2 = null;
	var $image3 = null;
	var $image4 = null;
	var $image5 = null;
	var $image6 = null;
	var $image7 = null;
	var $image8 = null;
	var $image9 = null;
	var $image10 = null;
	var $zip = null;
	var $youtube_code = null;
	var $on_stock = null;
	var $comments = null;
	var $delai_stock = null;
	var $etat_stock = null;
	var $affiche_stock = null;
	var $on_special = null;
	var $on_rupture = null;
	var $on_flash = null;
	var $on_gift = null;
	var $on_gift_points = null;
	var $flash_start = null;
	var $flash_end = null;
	var $prix_flash = null;
	var $extrait = null;
	var $on_download = null;
	var $on_check = null;
	var $on_reseller = null;
	var $id_marque = null;
	var $default_color_id = null;
	var $display_tab = null;
	// categorie_id is the id of one of the categories the products belongs to.
	var $categorie_id = null;
	// categorie is the name of one of the categories of the product
	var $categorie = null;
	var $id_ecotaxe = null;
	var $ecotaxe_ttc = null;
	var $ecotaxe_ht = null;
	// Configuration du produit
	var $configuration_color_id = null;
	var $configuration_size_id = null;
	var $configuration_attributs_list = "";
	var $configuration_total_original_price_attributs_ht = 0;
	var $configuration_attributs_description = "";
	var $configuration_size_name = "";
	var $configuration_overweight = null;
	var $configuration_size_price_ht = 0;
	// Id du fournisseur
	var $id_utilisateur = null;
	var $vat_applicable = null;
	var $extra_link = null;
	var $conditionnement = null;
	var $categorie_sentence_displayed_on_product = null;
	var $date_maj = null;
	var $attributes_with_single_options_array = null;
	var $paiement = null;

	/**
	 * Product::Product()
	 *
	 * @param integer $id Id ou code technique (si code technique et qu'il n'est pas unique en BDD, on prend le premier produit trouvé avec ce code)
	 * @param array $product_infos
	 * @param boolean $user_only_product_infos
	 * @param string $lang
	 * @param boolean $show_all_etat_if_admin
	 * @param boolean $vat_applicable
	 */
	function Product($id, $product_infos = null, $user_only_product_infos = false, $lang = null, $show_all_etat_if_admin = true, $vat_applicable = true, $show_all = false)
	{
		if (empty($lang)) {
			$lang = $_SESSION['session_langue'];
		}
		if (is_object($product_infos)) {
			$product_infos = get_object_vars($product_infos);
		}
		$this->lang = $lang;
		$lang_items = array('name' => 'nom_'.(!empty($GLOBALS['site_parameters']['product_name_forced_lang'])?$GLOBALS['site_parameters']['product_name_forced_lang']:$lang), 'descriptif' => 'descriptif_' . $lang, 'description' => 'description_' . (!empty($GLOBALS['site_parameters']['product_description_forced_lang'])?$GLOBALS['site_parameters']['product_description_forced_lang']:$lang), 'meta_titre' => 'meta_titre_' . $lang, 'meta_desc' => 'meta_desc_' . $lang, 'meta_key' => 'meta_key_' . $lang);
		if (empty($id)) {
			$this->id = vn($product_infos['id']);
		} else {
			if(!is_numeric($id)) {
				$sql = "SELECT p.id
					FROM peel_produits p
					WHERE p.technical_code = '" . real_escape_string($id) . "' AND " . get_filter_site_cond('produits', 'p') . "
					LIMIT 1";
				$query = query($sql);
				if($result = fetch_assoc($query)) {
					$this->id = $result['id'];
				} else {
					$this->id = false;
				}
			} else {
				$this->id = $id;
			}
		}
		if (!empty($product_infos)) {
			// Faster than making an SQL request if we have data already available
			foreach(array_keys(get_object_vars($this)) as $this_item) {
				if (isset($product_infos[$this_item]) && !in_array($this_item, array('id', 'lang'))) {
					$this->$this_item = $product_infos[$this_item];
				} elseif (!empty($lang_items[$this_item]) && isset($product_infos[$lang_items[$this_item]])) {
					$this->$this_item = $product_infos[$lang_items[$this_item]];
				}
			}
		}
		if (is_module_gift_checks_active()) {
			// Il faut déterminer si le produit est un chèque cadeaux. Les chèques cadeaux n'ont pas de catégorie associée, donc il faut modifier la requête SQL de cette classe en conséquence pour ne pas faire de jointure INNER sur les catégories (même effet que la variable global allow_products_without_category)
			$query = query("SELECT on_check
				FROM peel_produits
				WHERE id = '" . intval($id) . "' AND " . get_filter_site_cond('produits'));
			if ($result = fetch_assoc($query)) {
				$this->on_check = $result['on_check'];
			}
		}

		if (!$user_only_product_infos) {
			if(empty($GLOBALS['site_parameters']['use_ads_as_products'])) {
				$sql = "SELECT
						 p.id
						, p.technical_code
						, p.reference
						, p.ean_code
						, p.nom_".(!empty($GLOBALS['site_parameters']['product_name_forced_lang'])?$GLOBALS['site_parameters']['product_name_forced_lang']:$lang)." AS name
						, p.descriptif_" . $lang . " AS descriptif
						, p.description_" . (!empty($GLOBALS['site_parameters']['product_description_forced_lang'])?$GLOBALS['site_parameters']['product_description_forced_lang']:$lang) . " AS description
						, p.meta_titre_" . $lang . " AS meta_titre
						, p.meta_desc_" . $lang . " AS meta_desc
						, p.meta_key_" . $lang . " AS meta_key
						, p.on_estimate
						, p.prix
						, p.prix_achat
						, p.prix_revendeur
						, p.tva
						, p.etat
						, p.promotion
						, p.points
						, p.default_image
						, p.image1
						, p.image2
						, p.image3
						, p.image4
						, p.image5
						, p.image6
						, p.image7
						, p.image8
						, p.image9
						, p.image10
						, p.zip
						, p.id_utilisateur
						, p.youtube_code
						, p.on_stock
						, p.comments
						, p.delai_stock
						, p.etat_stock
						, p.affiche_stock
						, p.on_special
						, p.on_gift
						, p.on_gift_points
						, p.on_rupture
						, p.on_flash
						, p.flash_start
						, p.flash_end
						, p.prix_flash
						, p.extrait
						, p.on_download
						, p.on_check
						, p.on_reseller
						, p.id_marque
						, p.default_color_id
						, p.display_price_by_weight
						, p.id_ecotaxe
						, p.display_tab
						, p.poids
						, p.volume
						, p.position
						, p.extra_link
						, p.paiement
						, c.id AS categorie_id
						, c.nom_" . $lang . " AS categorie";
				if (is_conditionnement_module_active()) {
					$sql .= ", p.conditionnement";
				}
				if (!empty($GLOBALS['site_parameters']['enable_categorie_sentence_displayed_on_product'])) {
					$sql .= ", c.sentence_displayed_on_product_" . $lang . " AS categorie_sentence_displayed_on_product";
				}
				$sql .= " FROM peel_produits p
					" . (!empty($GLOBALS['site_parameters']['allow_products_without_category']) || $this->on_check == 1 ? 'LEFT' : 'INNER') . " JOIN peel_produits_categories pc ON pc.produit_id=p.id
					" . (!empty($GLOBALS['site_parameters']['allow_products_without_category']) || $this->on_check == 1 ? 'LEFT' : 'INNER') . " JOIN peel_categories c ON c.id = pc.categorie_id AND " . get_filter_site_cond('categories', 'c') . "
					WHERE p.id = '" . intval($this->id) . "' AND " . get_filter_site_cond('produits', 'p') . " " . (empty($show_all)?($show_all_etat_if_admin && a_priv("admin_products", false)?'AND p.etat IN ("1","0")':'AND p.etat = "1"') :'') . "
					LIMIT 1";
				// Le limit 1 est nécessaire car le produit peut être associé à plusieurs catégories => on ne récupère que la première catégorie trouvée
				$query = query($sql);
				$product_infos = fetch_assoc($query);
			} else {
				$ad_object = new Annonce($this->id);
				$product_infos = $ad_object->get_product_infos_object();
				unset($ad_object);
			}
			if (!empty($product_infos)) {
				foreach($product_infos as $this_item => $this_value) {
					if ($this->$this_item === null) {
						$this->$this_item = $this_value;
					}
				}
			} else {
				// If the product does not exist, its id is put to 0 even if $id is not 0
				$this->id = 0;
			}
		}
		if (!empty($GLOBALS['site_parameters']['specific_categorie_used_for_product_array'])) {
			// Cette variable de configuration permet de spécifier une catégorie différente de l'association en back office. Cela permet par exemple de choisir la catégorie avec laquelle l'url du produit sera générée, dans le cas où plusieurs catégories sont associées au produit.
			// Il est possible de créer une règle qui s'applique à tous les produits en définissant le paramètre comme ceci : '*'=>'id_de_categorie'
			// On peut aussi définir une règle différente produit par produit : 'id_de_produit1'=>'id_de_categorie1','id_de_produit2'=>'id_de_categorie2','id_de_produit3'=>'id_de_categorie3'
			// faire un mélange des deux, la priorité est faite sur la configuration spécifique à un produit : '*'=>'id_de_categorie','id_de_produit1'=>'id_de_categorie1', 'id_de_produit2'=>'id_de_categorie2'
			// Cette configuration est incompatible avec allow_multiplie_product_url_with_categorie === true.
			if (!empty($GLOBALS['site_parameters']['specific_categorie_used_for_product_array'][$this->id])) {
				// une règle est défini pour ce produit, il faut spécifier la catégorie choisie
				$this->categorie_id = $GLOBALS['site_parameters']['specific_categorie_used_for_product_array'][$this->id];
				$this->categorie = get_category_name($GLOBALS['site_parameters']['specific_categorie_used_for_product_array'][$this->id]);
			} elseif(!empty($GLOBALS['site_parameters']['specific_categorie_used_for_product_array']['*'])) {
				// une règle général s'applique
				$this->categorie_id = $GLOBALS['site_parameters']['specific_categorie_used_for_product_array']['*'];
				$this->categorie = get_category_name($GLOBALS['site_parameters']['specific_categorie_used_for_product_array']['*']);
			}
		}
		$this->name = String::html_entity_decode_if_needed($this->name);
		$this->descriptif = String::html_entity_decode_if_needed($this->descriptif);
		$this->description = String::html_entity_decode_if_needed($this->description);
		
		if(function_exists('get_product_description')) {
			$this->description .= get_product_description($this);
		}
		correct_output($this->descriptif, false, 'html', $lang);
		correct_output($this->description, false, 'html', $lang);
		// On ajoute à la description les attributs à options uniques, puisque ces attributs ne seront pas sélectionnables par ailleurs (car rien à sélectionner)
		$possible_attributes_with_single_options = $this->get_possible_attributs('infos', false, get_current_user_promotion_percentage(), display_prices_with_taxes_active(), is_reseller_module_active() && is_reseller(), true, true, false, true);
		foreach($possible_attributes_with_single_options as $this_nom_attribut_id => $this_options_array) {
			foreach($this_options_array as $this_attribut_id => $this_options_infos) {
				if($this_attribut_id && empty($this_options_infos['texte_libre']) && empty($this_options_infos['upload'])) {
					// Ceci n'est pas un attribut texte ou upload
					if (empty($GLOBALS['site_parameters']['disable_display_attributes_with_single_options_on_product_description'])) {
						$this->description .= $this_options_infos['nom'] . $GLOBALS['STR_BEFORE_TWO_POINTS'] . ': ' . $this_options_infos['descriptif'] . '<br />';
					}
					$this->attributes_with_single_options_array[$this_options_infos['technical_code']] = array('nom'=>$this_options_infos['nom'],'descriptif'=>$this_options_infos['descriptif']);
				}
			}
		}
		$this->categorie = String::html_entity_decode_if_needed(vb($this->categorie));
		$this->poids = floatval($this->poids);
		$this->volume = floatval($this->volume);
		if (is_module_ecotaxe_active()) {
			if (!empty($product_infos) && isset($product_infos['ecotaxe_ht']) && isset($product_infos['ecotaxe_ttc'])) {
				$this->ecotaxe_ht = $product_infos['ecotaxe_ht'];
				$this->ecotaxe_ttc = $product_infos['ecotaxe_ttc'];
			} else {
				if (!empty($this->id_ecotaxe)) {
					$eco = get_ecotax_object($this->id_ecotaxe);
				}
				if (!empty($eco)) {
					$this->ecotaxe_ht = $eco->prix_ht;
					$this->ecotaxe_ttc = $eco->prix_ttc;
				} else {
					$this->ecotaxe_ht = 0;
					$this->ecotaxe_ttc = 0;
				}
			}
		} else {
			$this->ecotaxe_ht = 0;
			$this->ecotaxe_ttc = 0;
		}
		$this->prix_ht = $this->prix / (1 + $this->tva / 100);
		if (check_if_module_active('attributs')) {
			// On ajoute au prix les attributs à options uniques, puisque ces attributs ne seront pas sélectionnables par ailleurs (car rien à sélectionner)
			$price_calculation = affiche_attributs_form_part($this, 'price_calculation', null, null, null, null, null, is_reseller_module_active() && is_reseller(), false, false, true);
		}
		$this->prix_ht += vn($GLOBALS['last_calculation_additional_price_ht']);
		$this->vat_applicable = $vat_applicable;
		if (empty($vat_applicable)) {
			$this->ecotaxe_ttc = $this->ecotaxe_ht;
			$this->prix = $this->prix_ht;
		}else {
			$this->prix = $this->prix_ht * (1 + $this->tva / 100);
		}
		if(!empty($GLOBALS['site_parameters']['price_hide_if_not_loggued']) && (!est_identifie() || (!a_priv('util') && !a_priv('admin*') && !a_priv('reve'))) && !check_if_module_active('devis')) {
			$this->on_estimate = 1;
		}
	}

	/**
	 * Définit la configuration du produit, en tenant compte du statut revendeur ou non de l'utilisateur afin de stocker les bonnes valeurs dans configuration_size_price_ht et configuration_total_original_price_attributs_ht
	 * format_attribut_description_for_database est défini pour distinguer le cas où l'on veut sauvegarder dans la bdd ou pas
	 *
	 * @param integer $color_id
	 * @param integer $size_id
	 * @param mixed $attributs_list
	 * @param boolean $reseller_mode
	 * @param boolean $format_attribut_description_for_database
	 * @return
	 */
	function set_configuration($color_id = null, $size_id = null, $attributs_list = null, $reseller_mode = false, $format_attribut_description_for_database = false)
	{
		// Color has no impact on price
		$this->configuration_color_id = $color_id;
		if ($this->configuration_size_id !== $size_id) {
			// Size can have an impact on price
			$this->configuration_size_id = $size_id;
			$size_array = $this->get_size('infos', 0, false, $reseller_mode, false, false);
			$this->configuration_size_name = vb($size_array['name']);
			$this->configuration_size_price_ht = vn($size_array['row_original_price']);
			$this->configuration_overweight = vn($size_array['poids']);
		}
		if (check_if_module_active('attributs') && $this->configuration_attributs_list !== $attributs_list) {
			// Initialisation
			$this->configuration_attributs_list = $attributs_list;
			$this->configuration_total_original_price_attributs_ht = 0;
			$this->configuration_attributs_description = "";
			// Traitement des attributs
			if (!empty($attributs_list)) {
				$this->configuration_attributs_description = affiche_attributs_form_part($this, 'selected_text', null, null, null, null, null, $reseller_mode);
				$this->configuration_total_original_price_attributs_ht = vn($GLOBALS['last_calculation_additional_price_ht']);
			}
		}
	}

	/**
	 * Product::get_product_url()
	 *
	 * @param boolean $add_get_suffixe
	 * @param boolean $html_encode
	 * @return
	 */
	function get_product_url($add_get_suffixe = false, $html_encode = false)
	{
		if(empty($GLOBALS['site_parameters']['use_ads_as_products'])) {
			if (empty($this->categorie_id) || empty($this->categorie)) {
				$query = query("SELECT p.nom_".(!empty($GLOBALS['site_parameters']['product_name_forced_lang'])?$GLOBALS['site_parameters']['product_name_forced_lang']:$this->lang)." AS name, pc.categorie_id, r.nom_" . $this->lang . " AS categorie
					FROM peel_produits p
					" . (!empty($GLOBALS['site_parameters']['allow_products_without_category']) || $this->on_check == 1 ? 'LEFT' : 'INNER') . " JOIN peel_produits_categories pc ON p.id = pc.produit_id
					" . (!empty($GLOBALS['site_parameters']['allow_products_without_category']) || $this->on_check == 1 ? 'LEFT' : 'INNER') . " JOIN peel_categories r ON r.id = pc.categorie_id AND " . get_filter_site_cond('categories', 'r') . "
					WHERE p.id ='" . intval($this->id) . "' AND " . get_filter_site_cond('produits', 'p') . "
					LIMIT 1");
				if ($prod = fetch_assoc($query)) {
					$this->categorie_id = $prod['categorie_id'];
					$this->categorie = $prod['categorie'];
					if(empty($this->name)) {
						$this->name = $prod['name'];
					}
				}
			}
			if (!empty($this->categorie_id)) {
				return get_product_url($this->id, $this->name, $this->categorie_id, $this->categorie, $add_get_suffixe, $html_encode);
			} else {
				return get_product_url($this->id, $this->name, 0, null, $add_get_suffixe, $html_encode);
			}
		} else {
			$ad_object = new Annonce($this->id);
			$url = $ad_object->get_annonce_url();
			unset($ad_object);
			return $url;
		}
	}

	/**
	 * Product::get_color()
	 *
	 * @return
	 */
	function get_color()
	{
		$colors_array = $this->get_possible_colors();
		if (!empty($colors_array[$this->configuration_color_id])) {
			return $colors_array[$this->configuration_color_id];
		} else {
			return null;
		}
	}

	/**
	 * Product::get_possible_colors()
	 *
	 * @return
	 */
	function get_possible_colors()
	{
		static $possible_colors;
		$cache_id = $this->id . '-' . $this->lang;
		// Utilisation de array_keys car isset($possible_colors[$cache_id]) renvoie faux si c'est défini mais vaut null.
		if(empty($possible_colors) || !in_array($cache_id, array_keys($possible_colors))){
			$possible_colors[$cache_id] = array();
			$query = query('SELECT pc.couleur_id, c.nom_' . $this->lang . '
				FROM peel_produits_couleurs pc
				INNER JOIN peel_couleurs c ON c.id = pc.couleur_id AND ' .  get_filter_site_cond('couleurs', 'c') . ' 
				WHERE pc.produit_id  = "' . intval($this->id) . '" 
				ORDER BY c.position ASC, c.nom_' . $this->lang . ' ASC');
			while ($result = fetch_assoc($query)) {
				$possible_colors[$cache_id][$result['couleur_id']] = $result['nom_' . $this->lang];
			}
		}
		return $possible_colors[$cache_id];
	}

	/**
	 * Product::get_size()
	 *
	 * @param string $return_mode
	 * @param integer $user_promotion_percentage
	 * @param boolean $with_taxes
	 * @param boolean $reseller_mode
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @return
	 */
	function get_size($return_mode = 'name', $user_promotion_percentage = 0, $with_taxes = true, $reseller_mode = false, $format = false, $add_tax_type_text = false)
	{
		$sizes_array = $this->get_possible_sizes($return_mode, $user_promotion_percentage, $with_taxes, $reseller_mode, $format, $add_tax_type_text);
		if (!empty($sizes_array[$this->configuration_size_id])) {
			return $sizes_array[$this->configuration_size_id];
		} else {
			return null;
		}
	}

	/**
	 * Product::get_possible_sizes()
	 *
	 * @param string $return_mode
	 * @param integer $user_promotion_percentage
	 * @param boolean $with_taxes
	 * @param boolean $reseller_mode
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @return
	 */
	function get_possible_sizes($return_mode = 'name', $user_promotion_percentage = 0, $with_taxes = true, $reseller_mode = false, $format = false, $add_tax_type_text = false)
	{
		static $possible_sizes;
		$sizes_array = array();
		// Utilisation de array_keys car isset($possible_sizes[$this->id . '-' . $this->lang]) renvoie faux si c'est défini mais vaut null.
		if(empty($possible_sizes) || !in_array($this->id . '-' . $this->lang, array_keys($possible_sizes))){
			$possible_sizes[$this->id . '-' . $this->lang] = array();
			$query = query('SELECT t.*, pt.taille_id
				FROM peel_produits_tailles pt
				INNER JOIN peel_tailles t ON t.id=pt.taille_id AND ' .  get_filter_site_cond('tailles', 't') . '
				WHERE pt.produit_id="' . intval($this->id) . '"
				ORDER BY t.position ASC, t.prix ASC, t.nom_' . $this->lang . ' ASC');
			while ($result = fetch_assoc($query)) {
				$possible_sizes[$this->id . '-' . $this->lang][] = $result;
			}
		}
		if (!empty($possible_sizes) && !empty($possible_sizes[$this->id . '-' . $this->lang])) {
			foreach($possible_sizes[$this->id . '-' . $this->lang] as $result) {
				if ($return_mode == 'name') {
					$sizes_array[$result['taille_id']] = $result['nom_' . $this->lang];
				} else {
					if ($reseller_mode && a_priv('reve') && $result["prix_revendeur"] != 0) {
						$original_price = $result["prix_revendeur"] / (1 + $this->tva / 100);
					} else {
						$original_price = $result["prix"] / (1 + $this->tva / 100);
					}
					$final_price = $original_price * (1 - $this->get_all_promotions_percentage($reseller_mode, $user_promotion_percentage) / 100);
					$result['name'] = $result['nom_' . $this->lang];
					$result['row_original_price'] = $this->format_prices($original_price, $with_taxes, false, false, false);
					$result['row_final_price'] = $this->format_prices($final_price, $with_taxes, false, false, false);
					$result['final_price_formatted'] = $this->format_prices($final_price, $with_taxes, false, $format, $add_tax_type_text);
					$sizes_array[$result['taille_id']] = $result;
				}
			}
		}
		return $sizes_array;
	}

	/**
	 * Product::get_possible_attributs()
	 *
	 * @param string $return_mode Values allowed : 'infos', 'rough', 'option_name', 'full_name'
	 * @param boolean $get_configuration_results_only
	 * @param integer $user_promotion_percentage
	 * @param boolean $with_taxes
	 * @param boolean $reseller_mode
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @param boolean $get_attributes_with_multiple_options_only
	 * @param boolean $get_attributes_with_single_options_only
	 * @param string $filter_technical_code
	 * @return
	 */
	function get_possible_attributs($return_mode = 'name', $get_configuration_results_only = false, $user_promotion_percentage = 0, $with_taxes = true, $reseller_mode = false, $format = false, $add_tax_type_text = false, $get_attributes_with_multiple_options_only = true, $get_attributes_with_single_options_only = false, $filter_technical_code = null)
	{
		static $attributs_array;
		if (!check_if_module_active('attributs')) {
			return array();
		}
		$cache_id = md5(serialize(array($return_mode, $get_configuration_results_only, $user_promotion_percentage, $with_taxes, $reseller_mode, $format, $add_tax_type_text, $get_attributes_with_multiple_options_only, $get_attributes_with_single_options_only, $filter_technical_code, $this->id, $this->configuration_attributs_list)));
		// Utilisation de arra_keys car isset($attributs_array[$cache_id]) renvoie faux si c'est défini mais vaut null.
		if(empty($attributs_array) || !in_array($cache_id, array_keys($attributs_array))){
			if(!empty($this->id)) {
				$attributs_array[$cache_id] = get_possible_attributs($this->id, ($return_mode=='infos'?'rough':$return_mode), $get_attributes_with_multiple_options_only, $get_attributes_with_single_options_only, ($get_configuration_results_only?$this->configuration_attributs_list:null));
			} else {
				$attributs_array[$cache_id] = array();
			}
			if (!empty($attributs_array[$cache_id]) && $return_mode == 'infos') {
				foreach ($attributs_array[$cache_id] as $this_nom_attribut_id => $this_attribut_values_array) {
					foreach ($this_attribut_values_array as $this_attribut_id => $result) {
						if(!empty($filter_technical_code) && $result['technical_code'] == $filter_technical_code) {
							continue;
						}
						if ($reseller_mode && a_priv('reve') && $result["prix_revendeur"] != 0) {
							$original_price = $result["prix_revendeur"] / (1 + $this->tva / 100);
						} else {
							$original_price = $result["prix"] / (1 + $this->tva / 100);
						}
						$final_price = $original_price * (1 - $this->get_all_promotions_percentage($reseller_mode, $user_promotion_percentage) / 100);
						$result['name'] = $result['nom'];
						$result['row_original_price'] = $this->format_prices($original_price, $with_taxes, false, false, false);
						$result['row_final_price'] = $this->format_prices($final_price, $with_taxes, false, false, false);
						$result['final_price_formatted'] = $this->format_prices($final_price, $with_taxes, false, $format, $add_tax_type_text);
						$attributs_array[$cache_id][$this_nom_attribut_id][$this_attribut_id] = $result;
					}
				}
			}
		}
		return $attributs_array[$cache_id];
	}
	
	/**
	 * Product::get_product_references()
	 *
	 * @return
	 */
	function get_product_references()
	{
		$references_array = array();
		$sql = 'SELECT ppr.reference_id
			FROM peel_produits_references ppr
			WHERE ppr.produit_id="' . intval($this->id) . '"';
		$query = query($sql);
		while ($result = fetch_assoc($query)) {
			$references_array[] = $result['reference_id'];
		}
		return $references_array;
	}

	/**
	 * Product::get_product_brands()
	 *
	 * @param integer $return_array
	 * @return
	 */
	function get_product_brands($return_array = true)
	{
		static $brands_array;
		$cache_id = $this->id . '_' . $this->lang;
		if(empty($brands_array) || !in_array($cache_id, array_keys($brands_array))){
			$brands_array[$cache_id] = array();
			$query = query("SELECT pm.nom_" . $this->lang . "
				FROM peel_marques pm
				WHERE pm.id='" . intval($this->id_marque) . "' AND " . get_filter_site_cond('marques', 'pm'));
			while ($result = fetch_assoc($query)) {
				$brands_array[$cache_id][$this->id_marque] = $result['nom_' . $this->lang];
			}
			if(empty($brands_array[$cache_id]) && !empty($this->marque)) {
				$brands_array[$cache_id][] = $this->marque;
			}
		}
		if($return_array) {
			return $brands_array[$cache_id];
		} else {
			return implode(', ', $brands_array[$cache_id]);
		}
	}

	/**
	 * Product::get_product_options()
	 *
	 * @return
	 */
	function get_product_options()
	{
		$options_array = array();
		$query = query('SELECT pa.descriptif_' . $this->lang . '
			FROM peel_attributs pa
			INNER JOIN peel_produits_attributs ppa ON ppa.attribut_id = pa.id
			WHERE ppa.produit_id = ' . intval($this->id) .' AND ' . get_filter_site_cond('attributs', 'pa'));
		while ($result = fetch_assoc($query)) {
			$options_array[] = $result['descriptif_' . $this->lang];
		}
		return $options_array;
	}

	/**
	 * Check if a picture or a pdf exist in peel_produit_color and peel_produit and returns the first image file name
	 * if no picture for this product, it return 'false'
	 *
	 * @param boolean $display_pdf
	 * @param integer $force_id_couleur
	 * @return
	 */
	function get_product_main_picture($display_pdf = false, $force_id_couleur = null)
	{
		$product_images = $this->get_product_pictures($display_pdf, $force_id_couleur, true);
		if (!empty($product_images)) {
			return $product_images[0];
		} else {
			return false;
		}
	}
	/**
	 * Check if pictures or pdf files exist in peel_produit_color and peel_produit and returns the array of these file names
	 * if no picture for this product, it return 'false'
	 *
	 * @param boolean $display_pdf
	 * @param integer $force_id_couleur
	 * @param boolean $only_return_first_picture
	 * @return
	 */
	function get_product_pictures($display_pdf = false, $force_id_couleur = null, $only_return_first_picture = false)
	{
		static $product_images;
		$cache_id = md5(serialize(array($this->id, $this->configuration_color_id, $this->default_color_id, $display_pdf, $force_id_couleur, $only_return_first_picture)));
		if(empty($product_images) || !in_array($cache_id, array_keys($product_images))){
			if(empty($GLOBALS['site_parameters']['use_ads_as_products'])) {
				if (!empty($force_id_couleur)) {
					$sql_condition = ' AND couleur_id="' . intval($force_id_couleur) . '"';
				} elseif (!empty($this->configuration_color_id)) {
					$sql_condition = ' AND couleur_id="' . intval($this->configuration_color_id) . '"';
				} else {
					// Si il n'y a pas de couleur choisie, on sélectionne la couleur par défaut choisie par l'admin
					$sql_condition = ' AND couleur_id="' . intval($this->default_color_id) . '"';
				}
				if(empty($GLOBALS['site_parameters']['disable_product_colors'])) {
					$sql = 'SELECT *
						FROM peel_produits_couleurs
						WHERE produit_id="' . intval($this->id) . '" ' . $sql_condition . '
						LIMIT 1';
					$q = query($sql);
					if ($result = fetch_assoc($q)) {
						// On commence par l'image par défaut pour que ce soit le premier élément du tableau
						if (!empty($result['default_image']) && is_numeric($result['default_image']) && !empty($result['image' . $result['default_image']]) && ($display_pdf || pathinfo($result['image' . $result['default_image']], PATHINFO_EXTENSION) != 'pdf')) {
							$product_images[$cache_id][] = $result['image' . $result['default_image']];
						}
						for($i = 1;$i <= 5;$i++) {
							if (!empty($result['image' . $i]) && $i != $result['default_image'] && (!$only_return_first_picture || empty($product_images[$cache_id])) && ($display_pdf || pathinfo($result['image' . $i], PATHINFO_EXTENSION) != 'pdf')) {
								$product_images[$cache_id][] = $result['image' . $i];
							}
						}
					}
				}
				if($this->default_image === null) {
					// Produit chargé à partir de données transmises de l'extérieur => nécessite de compléter les informations
					$sql = 'SELECT default_image, image1, image2, image3, image4, image5, image6, image7, image8, image9, image10
						FROM peel_produits
						WHERE id=' . intval($this->id). " AND " . get_filter_site_cond('produits') . "";
					$q = query($sql);
					if ($result = fetch_assoc($q)) {
						foreach($result as $this_item => $this_value) {
							$this->$this_item = $this_value;
						}
					}
				}
				// On commence par l'image par défaut pour que ce soit le premier élément du tableau
				$this_image_item = 'image' . $this->default_image;
				if (!empty($this->default_image) && is_numeric($this->default_image) && !empty($this->$this_image_item) && (!$only_return_first_picture || empty($product_images[$cache_id])) && ($display_pdf || pathinfo($this->$this_image_item, PATHINFO_EXTENSION) != 'pdf')) {
					$product_images[$cache_id][] = $this->$this_image_item;
				}
				for($i = 1;$i <= 10;$i++) {
					$this_image_item = 'image' . $i;
					if (!empty($this->$this_image_item) && $i != $this->default_image && (!$only_return_first_picture || empty($product_images[$cache_id])) && ($display_pdf || pathinfo($this->$this_image_item, PATHINFO_EXTENSION) != 'pdf')) {
						$product_images[$cache_id][] = $this->$this_image_item;
					}
				}
			} else {
				$ad_object = new Annonce($this->id);
				$product_images[$cache_id][] = $ad_object->get_annonce_picture();
				unset($ad_object);
			}
			if (empty($product_images[$cache_id])) {
				$product_images[$cache_id] = false;
			}
		}
		return $product_images[$cache_id];
	}

	/**
	 * Product::get_supplier_price()
	 *
	 * @param boolean $with_taxes
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @param boolean $add_ecotax
	 * @param integer $quantity
	 * @return
	 */
	function get_supplier_price($with_taxes = true, $format = false, $add_tax_type_text = false, $add_ecotax = true, $quantity = 1)
	{
		if (isset($this->prix_achat)) {
			$prix_achat_ht = $this->prix_achat / (1 + $this->tva / 100);
			return $this->format_prices($prix_achat_ht, $with_taxes, (!empty($add_ecotax)?$quantity:false), $format, $add_tax_type_text);
		} else {
			return null;
		}
	}

	/**
	 * Product::get_original_price()
	 *
	 * @param boolean $with_taxes
	 * @param boolean $reseller_mode
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @param boolean $add_ecotax
	 * @param boolean $get_price_for_this_configuration
	 * @param integer $quantity
	 * @return
	 */
	function get_original_price($with_taxes = true, $reseller_mode = false, $format = false, $add_tax_type_text = false, $add_ecotax = true, $get_price_for_this_configuration = true, $quantity = 1, $prices_whole_site_rebate_percentage = true)
	{
		if ($reseller_mode && a_priv('reve') && $this->prix_revendeur != 0) {
			$price_ht = $this->prix_revendeur / (1 + $this->tva / 100);
		} else {
			$price_ht = $this->prix_ht;
		}
		if ($get_price_for_this_configuration) {
			$price_ht += $this->configuration_size_price_ht;
			if (check_if_module_active('attributs')) {
				$price_ht += $this->configuration_total_original_price_attributs_ht;
			}
		}
		if(!empty($GLOBALS['site_parameters']['prices_whole_site_rebate_percentage'])) {
			$price_ht = $price_ht * (1 - $GLOBALS['site_parameters']['prices_whole_site_rebate_percentage']/100);
		}
		$price_ht = $price_ht * $quantity;
		return $this->format_prices($price_ht, $with_taxes, (!empty($add_ecotax)?$quantity:false), $format, $add_tax_type_text);
	}

	/**
	 * Prix final après application des réductions diverses, pour la quantité demandée (on renvoie le prix total, et non pas le prix unitaire)
	 *
	 * @param integer $user_promotion_percentage
	 * @param boolean $with_taxes
	 * @param boolean $reseller_mode
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @param integer $quantity
	 * @param boolean $add_ecotax
	 * @param boolean $get_price_for_this_configuration
	 * @param boolean $add_rdfa_properties
	 * @param integer $quantity_all_products_in_category
	 * @return
	 */
	function get_final_price($user_promotion_percentage = 0, $with_taxes = true, $reseller_mode = false, $format = false, $add_tax_type_text = false, $quantity = 1, $add_ecotax = true, $get_price_for_this_configuration = true, $add_rdfa_properties = false, $quantity_all_products_in_category = null)
	{
		if($quantity_all_products_in_category === null) {
			$quantity_all_products_in_category = $quantity;
		}
		// Choix entre prix revendeur et prix public
		if ($reseller_mode) {
			// The reseller price is never affected by flash prices
			$price_ht = $this->get_original_price(false, true, false, false, false, false, 1, false);
		} elseif ($this->is_price_flash($reseller_mode)) {
			$price_ht = $this->prix_flash / (1 + $this->tva / 100);
		} else {
			$price_ht = $this->get_original_price(false, false, false, false, false, false, 1, false);
		}
		if (is_lot_module_active()) {
			/* Si le module de gestion des prix / quantité est actif */
			if (!isset($GLOBALS['cache']) || !isset($GLOBALS['cache']['lot_price_by_id']) || !isset($GLOBALS['cache']['lot_price_by_id'][$this->id])) {
				$GLOBALS['cache']['lot_price_by_id'][$this->id] = array();
				// Ces informations ne changent pas en cours d'exécution du script
				// => on met en cache global (pas static car l'objet sera peut-être recréé entre temps)
				$query = query("SELECT quantite, prix, prix_revendeur
					FROM peel_quantites q
					WHERE produit_id = '" . intval($this->id) . "' AND " . (is_reseller_module_active() && is_reseller()?'q.prix_revendeur>0':'q.prix>0') . " AND " . get_filter_site_cond('quantites', 'q') . " 
					ORDER BY quantite ASC");
				while ($Qte = fetch_assoc($query)) {
					$GLOBALS['cache']['lot_price_by_id'][$this->id][] = $Qte;
				}
			}
			foreach($GLOBALS['cache']['lot_price_by_id'][$this->id] as $Qte) {
				/* il existe des remises / quantité dans la base */
				if ($quantity >= $Qte['quantite']) {
					$price_ht = min ($price_ht, ($reseller_mode && $Qte['prix_revendeur'] != 0? $Qte['prix_revendeur'] / (1 + $this->tva / 100) : $Qte['prix'] / (1 + $this->tva / 100)));
				} else {
					break;
				}
			}
		}
		if ($get_price_for_this_configuration) {
			$price_ht += $this->configuration_size_price_ht;
			if (check_if_module_active('attributs')) {
				$price_ht += $this->configuration_total_original_price_attributs_ht;
			}
		}
		if (!$this->is_price_flash($reseller_mode)) {
			if (!$reseller_mode) {
				// Pour les revendeurs, on n'applique pas d'autre réduction que le pourcentage de réduction explicite pour cet utilisateur
				$promotion_devises = 0;
				if (check_if_module_active('category_promotion')) {
					$cat = get_category_promotion_by_product($this->id, $quantity_all_products_in_category);
					if (!empty($cat) && $cat['promotion_devises'] > 0) {
						// Réduction par marque en valeur et non pas en pourcentage
						$promotion_devises = max($promotion_devises, $cat['promotion_devises']);
					}
				}
				if (check_if_module_active('marques_promotion')) {
					$marque = get_marque_promotion($this->id_marque);
					if (!empty($marque) && $marque['promotion_devises'] > 0) {
						// Réduction par marque en valeur et non pas en pourcentage
						// Si on veut cumuler les réductions par produit, par marque et par catégorie, changer la ligne ci-dessous
						$promotion_devises = max($promotion_devises, $marque['promotion_devises']);
					}
				}
				$get_promotion_by_user_offer_object = $this->get_promotion_by_user_offer($quantity);
				if(!empty($get_promotion_by_user_offer_object) && $get_promotion_by_user_offer_object->prix>0) {
					$price_ht = min($price_ht, $get_promotion_by_user_offer_object->prix / (1 + $this->tva / 100));
				}
				$promotion_by_product_filter_object = $this->get_promotion_by_product_filter();
				if(!empty($promotion_by_product_filter_object)) {
					$promotion_devises = max($promotion_devises, $promotion_by_product_filter_object->remise_valeur);
				}
				$price_ht = max($price_ht - $promotion_devises / (1 + $this->tva / 100), 0);
			}
			// Application des réductions en pourcentages
			$price_ht = $price_ht * (1 - $this->get_all_promotions_percentage($reseller_mode, $user_promotion_percentage, false, $quantity, $quantity_all_products_in_category) / 100) ;
		} else {
			// Si c'est un prix flash, on n'applique pas les réductions en pourcentage ni en valeur
			// (mais sur les options, les pourcentages seront quand même appliqués - pas gérés ici)
			$price_ht = $price_ht * (1 - $user_promotion_percentage / 100) ;
		}
		if(!empty($GLOBALS['site_parameters']['all_prices_rebate_percentage'])) {
			$price_ht = $price_ht * (1 - $GLOBALS['site_parameters']['all_prices_rebate_percentage']/100);
		}
		$price_ht = $price_ht * $quantity;
		return $this->format_prices($price_ht, $with_taxes, (!empty($add_ecotax)?$quantity:false), $format, $add_tax_type_text, $add_rdfa_properties);
	}

	/**
	 * Product::is_price_flash()
	 *
	 * @param boolean $reseller_mode
	 * @return
	 */
	function is_price_flash($reseller_mode = false)
	{
		return (!$reseller_mode && check_if_module_active('flash') && $this->prix_flash > 0 && $this->flash_start < date('Y-m-d H:i:s', time()) && $this->flash_end > date('Y-m-d H:i:s', time()) && is_flash_active_on_site());
	}

	/**
	 * is_code_promo_applicable()
	 *
	 * @param integer $id_categorie
	 * @param string $product_filter
	 * @param boolean $found_cat
	 * @return
	 */
	function is_code_promo_applicable($id_categorie, $product_filter, &$found_cat, $cat_not_apply_code_promo = null)
	{
		$apply_code_on_this_product = true;
		if (!empty($id_categorie) || !empty($cat_not_apply_code_promo)) {
			$q_get_product_cat = query('SELECT categorie_id
				FROM peel_produits_categories ppc
				WHERE ppc.produit_id = "' . intval($this->id) . '"' .
				(!empty($id_categorie) ? 'AND ppc.categorie_id IN ("' . implode('","', nohtml_real_escape_string(get_category_tree_and_itself($id_categorie, 'sons'))) . '")' : '' ) .
				(!empty($cat_not_apply_code_promo) ? 'AND ppc.categorie_id NOT IN (' . str_replace(' ', '', implode(',', nohtml_real_escape_string(get_category_tree_and_itself($cat_not_apply_code_promo, 'sons')))) . ')' : '' ) .
				'LIMIT 1');
			if ($r_get_product_cat = fetch_assoc($q_get_product_cat)) {
				$found_cat = true;
			} else {
				$apply_code_on_this_product = false;
			}
		}
		if (!empty($product_filter)) {
			$found_product = false;
			foreach(array('description', 'reference') as $this_item) {
				if(String::strpos(String::strtolower($this->$this_item), String::strtolower($product_filter)) !== false) {
					$found_product = true;
					break;
				}
			}
			if(!$found_product) {
				$apply_code_on_this_product = false;
			}
		}
		return $apply_code_on_this_product;
	}

	/**
	 * Product::get_promotions_by_product_filter()
	 *
	 * @param boolean $reseller_mode
	 * @return
	 */
	function get_promotion_by_product_filter($reseller_mode = false)
	{
		static $promotion_by_product_id_array;
		if(empty($promotion_by_product_id_array) || !in_array($this->id, array_keys($promotion_by_product_id_array))){
			$sql = 'SELECT *
				FROM peel_codes_promos cp
				WHERE ' . get_filter_site_cond('codes_promos', 'cp') . ' AND nom="" AND cp.etat = "1" AND ("' . date('Y-m-d', time()) . '" BETWEEN cp.date_debut AND cp.date_fin) AND ("' . nohtml_real_escape_string(trim(String::substr($this->description,0,1024))) . '" LIKE CONCAT("%", product_filter, "%") OR "' . nohtml_real_escape_string(trim($this->reference)) . '" LIKE CONCAT("%", product_filter, "%")) 
				ORDER BY remise_percent DESC
				LIMIT 1';
			$query = query($sql);
			$promotion_by_product_id_array[$this->id] = fetch_object($query);
		}
		return $promotion_by_product_id_array[$this->id];
	}

	/**
	 * Product::get_promotion_by_user_offer()
	 *
	 * @param integer $quantity
	 * @return
	 */
	function get_promotion_by_user_offer($quantity = 1)
	{
		static $promotion_by_user_offer_array;
		if(!empty($GLOBALS['site_parameters']['user_offers_table_enable']) && !empty($_SESSION['session_utilisateur']['id_utilisateur'])) {
			$quantity_total = 0;
			$value_total = 0;
			foreach ($_SESSION['session_caddie']->articles as $numero_ligne => $product_id) {
				$product_object = new Product($product_id, null, false, null, true, $_SESSION['session_caddie']->apply_vat);
				if(empty($quantity_by_brand['brand_'.$product_object->get_product_brands(false)]) || $product_object->get_product_brands(false) == '') {
					$quantity_by_brand['brand_'.$product_object->get_product_brands(false)] = 0;
					$total_by_brand['brand_'.$product_object->get_product_brands(false)] = 0;
				}
				$quantity_by_brand['brand_'.$product_object->get_product_brands(false)] += $_SESSION['session_caddie']->quantite[$numero_ligne];
				$total_by_brand['brand_'.$product_object->get_product_brands(false)] += floatval($_SESSION['session_caddie']->quantite[$numero_ligne]*$product_object->prix_ht);
				$quantity_total += $_SESSION['session_caddie']->quantite[$numero_ligne];
				$value_total += floatval($_SESSION['session_caddie']->quantite[$numero_ligne]*$product_object->prix_ht);
				unset($product_object);
			}
			$sql = "SELECT o.*
				FROM peel_offres o
				LEFT JOIN peel_utilisateurs_offres uo ON uo.id_utilisateur='" . intval(vn($_SESSION['session_utilisateur']['id_utilisateur'])) . "' AND o.id_offre=uo.id_offre
				WHERE (o.id_offre=0 OR uo.id_offre IS NOT NULL) AND o.date_limite>='" . date('Y-m-d', time()) . "' AND (" . (!empty($this->reference)?"(o.ref='".real_escape_string($this->reference)."' AND o.qnte<='".intval($quantity)."' AND o.seuil<='".floatval(max($quantity*$this->prix_ht,vn($value_total)))."') OR ":"") . "(o.ref='' AND o.fournisseur IN ('".implode("','", real_escape_string($this->get_product_brands(true)))."') AND o.qnte<='".intval(max(vn($quantity_by_brand['brand_'.$this->get_product_brands(false)]), $quantity))."' AND o.seuil<='".floatval(max(vn($total_by_brand['brand_'.$this->get_product_brands(false)]), $quantity*$this->prix_ht, vn($value_total)))."') OR (o.ref='' AND o.fournisseur='' AND o.qnte<='".intval(max(vn($quantity_total), $quantity))."' AND o.seuil<='".floatval(max(vn($value_total), $quantity*$this->prix_ht))."'))
				ORDER BY " . (floatval($this->prix_ht)>0 ? "IF(o.prix>0 AND o.remise_percent>0, LEAST(o.prix, o.remise_percent*'".floatval($this->prix_ht)."'), IF(o.remise_percent>0, o.remise_percent*'".floatval($this->prix_ht)."', o.prix))" : "o.prix") . " DESC, o.remise_percent DESC
				LIMIT 1";
			$cache_id = md5($sql);
			if(empty($promotion_by_user_offer_array) || !in_array($cache_id, array_keys($promotion_by_user_offer_array))){
				$query = query($sql);
				$promotion_by_user_offer_array[$cache_id] = fetch_object($query);
			}
			return $promotion_by_user_offer_array[$cache_id];
		} else {
			return null;
		}
	}

	/**
	 * Product::get_all_promotions_percentage()
	 *
	 * @param boolean $reseller_mode
	 * @param integer $user_promotion_percentage
	 * @param boolean $format
	 * @param integer $quantity
	 * @param integer $quantity_all_products_in_category
	 * @return
	 */
	function get_all_promotions_percentage($reseller_mode = false, $user_promotion_percentage = 0, $format = false, $quantity = 1, $quantity_all_products_in_category = null)
	{
		if($quantity_all_products_in_category === null) {
			$quantity_all_products_in_category = $quantity;
		}
		$user_promotion_percentage = min($user_promotion_percentage, 100);
		if (!$reseller_mode) {
			// Pour les revendeurs, on n'applique pas d'autre réduction que le pourcentage de réduction explicite pour cet utilisateur
			if (check_if_module_active('category_promotion')) {
				$cat = get_category_promotion_by_product($this->id, $quantity_all_products_in_category);
			}
			if (empty($cat)) {
				$cat = array('nom' => '', 'promotion_devises' => 0, 'promotion_percent' => 0);
			}
			if (!empty($GLOBALS['site_parameters']['global_remise_percent'])) {
				$global_promotion = vn($GLOBALS['site_parameters']['global_remise_percent']);
			} else {
				$global_promotion = 0;
			}
			if (check_if_module_active('marques_promotion')) {
				$marque = get_marque_promotion($this->id_marque);
			}
			if (empty($marque)) {
				$marque = array('nom' => '', 'promotion_devises' => 0, 'promotion_percent' => 0);
			}
			$get_promotion_by_user_offer_object = $this->get_promotion_by_user_offer($quantity);
			if(!empty($get_promotion_by_user_offer_object)) {
				$promotion_by_user_offer = $get_promotion_by_user_offer_object->remise_percent;
			} else {
				$promotion_by_user_offer = 0;
			}
			$promotion_by_product_filter_object = $this->get_promotion_by_product_filter();
			if(!empty($promotion_by_product_filter_object)) {
				$promotion_by_product_filter = $promotion_by_product_filter_object->remise_percent;
			} else {
				$promotion_by_product_filter = 0;
			}
			if (!empty($GLOBALS['site_parameters']['product_add_all_percent_discount'])) {
				// Si on veut cumuler les réductions par produit, par marque et par catégorie
				$rebate_coefficient = 1 - (1 - $user_promotion_percentage / 100) * (1 - $this->promotion / 100) * (1 - $cat['promotion_percent'] / 100) * (1 - $marque['promotion_percent'] / 100) * (1 - $global_promotion / 100) * (1 - $promotion_by_product_filter / 100) * (1 - $promotion_by_user_offer / 100);
			} else {
				// La réduction produit est le max de ce qui est indiqué dans le produit, la marque , la catégorie et la promotion générale
				$rebate_coefficient = 1 - (1 - $user_promotion_percentage / 100) * (1 - min(max($this->promotion, $cat['promotion_percent'], $marque['promotion_percent'], $global_promotion, $promotion_by_product_filter, $promotion_by_user_offer), 100) / 100);
			}
		} else {
			// Si on est revendeur, seule la promotion utilisateur est utilisée
			$rebate_coefficient = 1 - (1 - $user_promotion_percentage / 100);
		}
		$percentage = $rebate_coefficient * 100;
		if ($format) {
			return sprintf("%0.2f", $percentage) . '%';
		} else {
			return $percentage;
		}
	}

	/**
	 * Product::get_ecotax()
	 *
	 * @param boolean $with_taxes
	 * @return
	 */
	function get_ecotax($with_taxes = true)
	{
		if ($with_taxes) {
			return $this->ecotaxe_ttc;
		} else {
			return $this->ecotaxe_ht;
		}
	}

	/**
	 * Product::affiche_prix()
	 *
	 * @param boolean $with_taxes
	 * @param boolean $reseller_mode
	 * @param boolean $return_mode
	 * @param boolean $display_with_measurement
	 * @param integer $item_id
	 * @param boolean $display_ecotax
	 * @param boolean $display_old_price
	 * @param string $table_css_class
	 * @param boolean $display_old_price_inline
	 * @return
	 */
	function affiche_prix($with_taxes = true, $reseller_mode = false, $return_mode = false, $display_with_measurement = false, $item_id = null, $display_ecotax = true, $display_old_price = true, $table_css_class = 'full_width', $display_old_price_inline = true, $add_rdfa_properties = false, $display_with_vat_symbol = true, $display_minimal_price = false)
	{
		$output = affiche_prix($this, $with_taxes, $reseller_mode, $return_mode, $display_with_measurement, $item_id, $display_ecotax, $display_old_price, $table_css_class, $display_old_price_inline, $display_with_vat_symbol, $add_rdfa_properties, $display_minimal_price);

		if ($return_mode) {
			return $output;
		} else {
			echo $output;
		}
	}

	/**
	 * Product::format_prices()
	 *
	 * @param float $value_without_taxes
	 * @param boolean $with_taxes
	 * @param integer $ecotax_quantity
	 * @param boolean $format
	 * @param boolean $add_tax_type_text
	 * @param boolean $add_rdfa_properties
	 * @return
	 */
	function format_prices($value_without_taxes, $with_taxes = true, $ecotax_quantity = 1, $format = false, $add_tax_type_text = false, $add_rdfa_properties = false)
	{
		if (display_prices_with_taxes_active()) {
			// On doit arrondir les valeurs tarifaires officielles qui sont en TTC
			$value_with_taxes = round($value_without_taxes * (1 + $this->tva / 100), 2);
			$value_without_taxes = $value_with_taxes / (1 + $this->tva / 100);
		} else {
			// On doit arrondir les valeurs tarifaires officielles qui sont en HT
			$value_without_taxes = round($value_without_taxes, 2);
			$value_with_taxes = $value_without_taxes * (1 + $this->tva / 100);
		}
		if (!empty($ecotax_quantity)) {
			$value_without_taxes += $this->get_ecotax(false) * $ecotax_quantity;
			$value_with_taxes += $this->get_ecotax(true) * $ecotax_quantity;
		}
		if ($with_taxes) {
			if (empty($this->vat_applicable)) {
				$value_with_taxes = $value_without_taxes;
			}
			if ($format) {
				$value_with_taxes = fprix($value_with_taxes, true, null, true, null, false, true, ',', $add_rdfa_properties);
				if ($add_tax_type_text) {
					$value_with_taxes .= ' ' . $GLOBALS['STR_TTC'];
				}
			}
			return $value_with_taxes;
		} else {
			if ($format) {
				$value_without_taxes = fprix($value_without_taxes, true, null, true, null, false, true, ',', $add_rdfa_properties);
				if ($add_tax_type_text) {
					$value_without_taxes .= ' ' . $GLOBALS['STR_HT'];
				}
			}
			return $value_without_taxes;
		}
	}

	/**
	 * Retoune le nom du fournisseur connaissant son id
	 *
	 * @return
	 */
	function get_supplier_name()
	{
		$user = get_user_information($this->id_utilisateur);
		if (!empty($user['societe'])) {
			return $user['societe'];
		} else {
			return false;
		}
	}

	/**
	 * Renvoie l'état du stock pour ce produit sous forme de HTML
	 *
	 * @param array $product_stock_infos
	 * @return
	 */
	function get_product_stock_state($product_stock_infos = null)
	{
		if (empty($product_stock_infos)) {
			$product_stock_infos = get_product_stock_infos($this->id);
		}
		$stock_remain_all = 0;
		foreach($product_stock_infos as $stock_infos) {
			$stock_remain_all += $stock_infos['stock_temp'];
		}
		return affiche_etat_stock($stock_remain_all, $this->on_rupture, true);
	}

	/**
	 * Product::get_possible_categories()
	 *
	 * @return
	 */
	function get_possible_categories()
	{
		$categories_array = array();
		$query = query('SELECT pc.categorie_id, c.nom_' . $this->lang . '
			FROM peel_produits_categories pc
			INNER JOIN peel_categories c ON c.id = pc.categorie_id AND ' . get_filter_site_cond('categories', 'c') . '
			WHERE pc.produit_id  = "' . intval($this->id) . '"
			ORDER BY c.position ASC, c.nom_' . $this->lang . ' ASC');
		while ($result = fetch_assoc($query)) {
			$categories_array[$result['categorie_id']] = $result['nom_' . $this->lang];
		}
		return $categories_array;
	}

	/**
	 * permet de savoir le nombre d'avis pour le produit
	 *
	 * @return
	 */
	function get_count_opinion()
	{
		$query = query("SELECT COUNT(*) as count_opinion
			FROM peel_avis pa
			WHERE pa.id_produit = '" . intval($this->id) . "' AND etat=1");
		$result = fetch_assoc($query);
		return vn($result['count_opinion']);
	}

	/**
	 * Récupère une image avec le QRCode
	 *
	 * @return
	 */
	function qrcode_image_src()
	{
		return $GLOBALS['wwwroot'].'/qrcode.php?path='.urlencode(rawurldecode(str_replace($GLOBALS['wwwroot'], '', $this->get_product_url())));
	}

	/**
	 * Retourne le prix d'appel du produit, toutes réductions inclue
	 *
	 * @return
	 */
	function get_minimal_price()
	{
		// recherche dans les prix par lot
		$sql = "SELECT MIN(prix) AS prix, MIN(prix_revendeur) AS prix_revendeur
			FROM peel_quantites
			WHERE produit_id = '" . intval($this->id) . "' AND "  . get_filter_site_cond('quantites');
		$query = query($sql);
		$Qte = fetch_assoc($query);
		$price_Qte_ht = (is_reseller_module_active() && is_reseller() && $Qte['prix_revendeur'] != 0? $Qte['prix_revendeur'] / (1 + $this->tva / 100) : $Qte['prix'] / (1 + $this->tva / 100));
		$price_ht = $this->get_final_price(get_current_user_promotion_percentage(), false, is_reseller_module_active() && is_reseller());
		$minimal_price_array = array();
		if ($price_Qte_ht > 0) {
			$minimal_price_array[] = $price_Qte_ht;
		}
		if ($price_ht > 0) {
			$minimal_price_array[] = $price_ht;
		}
		$minimal_price = min ($minimal_price_array);
		return $this->format_prices($minimal_price, display_prices_with_taxes_active(), false, true, true);
	}
}
